#include "stdafx.h"
#include "ParticleSystem.h"
#include "Camera.h"

ParticleSystem::ParticleSystem()
{
	m_totalTime = 0;
	m_Status = ParticleStatus::STOPPED;
}


ParticleSystem::~ParticleSystem()
{
}

int ParticleSystem::Init(float lifeTime, ParticleType type)
{

	char tga[] = "explosion.tga";
	char file_FS[] = "../Resources/Shaders/ParticleFS.fsh";
	char file_VS[] = "../Resources/Shaders/ParticleVS.vsh";
	m_lifeTime = lifeTime;
	glGenBuffers(1, &vbo);
	glGenTextures(1, &texture_id);
	glBindTexture(GL_TEXTURE_2D, texture_id);
	int width, height, bpp;
	char* imgData = LoadTGA(tga, &width, &height, &bpp);
	//
	if (bpp == 24)
		//RGB
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, imgData);
	else
		//RGBA
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imgData);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	delete[] imgData;

	srand(0);
	Particle particles[NUM_PARTICLES];

	for (int i = 0; i < NUM_PARTICLES; ++i)
	{
		if (type == ParticleType::EXPLOSION)
		{
			particles[i].Velocity.x = sin(float(i) * PI / 180);
			particles[i].Velocity.y = cos(float(i) * PI / 180);
			particles[i].Velocity.z = (rand() % 3 - 1) * float(rand() % 1000) / 1000.0f;
			particles[i].Velocity.Normalize();
			particles[i].Gravity = Vector3(0, -1, 0);
		}
		else if (type == ParticleType::BEAM)
		{
			// Calculate target and velocity at runtime
		}
		particles[i].LifeTime = lifeTime;
	}

	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	glBufferData(GL_ARRAY_BUFFER, sizeof(particles), particles, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	myShaders.Init(file_VS, file_FS);

	a_posL = glGetAttribLocation(myShaders.program, "a_posL");
	a_velocityL = glGetAttribLocation(myShaders.program, "a_velocityL");
	u_lifetime = glGetUniformLocation(myShaders.program, "u_lifetime");
	u_mvp = glGetUniformLocation(myShaders.program, "u_mvp");
	u_texture = glGetUniformLocation(myShaders.program, "u_texture");
	u_gravityW = glGetUniformLocation(myShaders.program, "u_gravityW");
	u_totaltime = glGetUniformLocation(myShaders.program, "u_totaltime");
	u_distanceToCamera = glGetUniformLocation(myShaders.program, "u_distanceToCamera");
	return 0;
}


void ParticleSystem::Update(float deltaTime)
{
	if (m_Status == ParticleStatus::STOPPED)
	{
		m_totalTime = 0;
		return;
	}
	m_totalTime += deltaTime;
	if (m_totalTime > m_lifeTime)
		Stop();
}


void ParticleSystem::Draw(Camera* camera)
{
	if (m_Status == ParticleStatus::STOPPED)
		return;
	glUseProgram(myShaders.program);
	// Bind buffer
	glBindBuffer(GL_ARRAY_BUFFER, vbo);

	glDepthMask(GL_FALSE);

	if (-1 != a_posL)
	{
		glEnableVertexAttribArray(a_posL);
		glVertexAttribPointer(a_posL, 3, GL_FLOAT, GL_FALSE, sizeof(Particle), 0);
	}
	if (-1 != a_velocityL)
	{
		glEnableVertexAttribArray(a_velocityL);
		glVertexAttribPointer(a_velocityL, 3, GL_FLOAT, GL_FALSE, sizeof(Particle), (char*)0 + sizeof(Vector3));
	}
	if (-1 != u_gravityW)
	{
		glEnableVertexAttribArray(u_gravityW);
		glVertexAttribPointer(u_gravityW, 3, GL_FLOAT, GL_FALSE, sizeof(Particle), (char*)0 + 2 * sizeof(Vector3));
	}
	if (-1 != u_lifetime)
	{
		glUniform1f(u_lifetime, m_lifeTime);
	}
	if (-1 != u_mvp)
	{
		Matrix mvp = camera->getViewMatrix() * camera->getProjectionMatrix();
		glUniformMatrix4fv(u_mvp, 1, GL_FALSE, &(mvp.m[0][0]));
	}
	if (-1 != u_texture)
	{
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, texture_id);
		glUniform1i(u_texture, 0);
	}

	if (-1 != u_totaltime)
	{
		glUniform1f(u_totaltime, m_totalTime);
	}

	if (-1 != u_distanceToCamera)
	{
		float d = (camera->getPosition() - m_Position).Length();
		glUniform1f(u_distanceToCamera, d);
	}
	glDrawArrays(GL_POINTS, 0, NUM_PARTICLES);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindTexture(GL_TEXTURE_2D, 0);
	glDepthMask(GL_TRUE);
}
